#ifndef __PVMVOICEIMAADPCMWAV_H__
#define __PVMVOICEIMAADPCMWAV_H__

#include "pvm/pvmapi.h"

#include "lib/pvmvoice.h"
#include "lib/decoders/wav/pvmdatasourcewav.h"

#include "lib/memory/pvmmemory.h"

class PVMVoiceIMAAPDPCMWAV : public IPVMVoice, public PVMAllocableObject
{
protected:
	PVMVoiceIMAAPDPCMWAV();
public:
	virtual ~PVMVoiceIMAAPDPCMWAV();

	virtual void GetFormat(pvm_uint32 &samplesPerSecond, pvm_uint32 &bitsperSample, pvm_uint32 &chanels);
	virtual pvm_uint32 GetSamplesPerSecond();
	virtual pvm_uint32 GetBitsPerSample();
	virtual pvm_uint32 GetChanelsNum();

	virtual pvm_uint32 FillBuffer(void *buffer, pvm_uint32 numSamples);
	virtual void SetPause(pvm_bool pause);
	virtual pvm_bool GetPause();
	virtual pvm_bool IsAlive();
	virtual void DoDestroy();

	virtual void OnIdle();

	virtual void SetLoopCount(pvm_int32 loopCount);
	virtual pvm_int32 GetLoopCount();
	virtual pvm_bool SetLoopInSamples(pvm_int64 loopFrom, pvm_int64 loopTo);
	virtual pvm_bool GetLoopInSamples(pvm_int64 &loopFrom, pvm_int64 &loopTo);
	virtual pvm_bool GetLoopInMS(pvm_int32 &loopFrom, pvm_int32 &loopTo);

	virtual pvm_bool SetPositionMS(pvm_int32 position);
	virtual pvm_int32 GetPositionMS();

	virtual pvm_bool GetOption(const pvm_char* optionName, void* optionVal, pvm_uint32* optionSize);
	virtual pvm_bool SetOption(const pvm_char* optionName, void* optionVal, pvm_uint32 optionSize);

	static PVMVoiceIMAAPDPCMWAV* Create(PVMDataSourceWAV* dataSource, int samplesPerSecond, int bitsPerSample, int chanelsNum);
protected:
	bool Init(PVMDataSourceWAV* source);
protected:
	PVMDataSourceWAV*	dataSource;

	pvm_bool	isAlive;
	pvm_bool	paused;
	pvm_int32	loopCount;
	pvm_int64	loopStart;
	pvm_int64	loopEnd;
	pvm_uint8	curNibblesPack;
	pvm_int		curNibblesNum;
	pvm_int		stepIndex;
	pvm_int32	predictedValue;

	static const pvm_int ima_index_table[16];
	static const pvm_int ima_step_table[89];
protected:
	inline pvm_int16 decodeSample(pvm_uint8 nibble)
	{
		pvm_int step = ima_step_table[stepIndex];
		stepIndex += ima_index_table[nibble];

		if (stepIndex < 0)
			stepIndex = 0;
		else if (stepIndex > 88)
			stepIndex = 88;

		pvm_int diff = step >> 3;

		if (nibble & 4)
			diff += step;
		if (nibble & 2)
			diff += step >> 1;
		if (nibble & 1)
			diff += step >> 2;
		if (nibble & 8)
			predictedValue -= diff;
		else
			predictedValue += diff;

		if ((predictedValue) < -0x8000)
			(predictedValue) = -0x8000;
		else if ((predictedValue) > 0x7fff)
			(predictedValue) = 0x7fff;

		return predictedValue;
	}

	pvm_bool SetPositionInSamples(pvm_int64 position);
};

#endif
